`ifndef CTRL_UNIT
`define CTRL_UNIT

`define ASSIGN_ZERO begin \
	sel_PC = 'h0; \
	sel_R0 = 'h0; \
	sel_R1 = 'h0; \
	sel_R2 = 'h0; \
	sel_R3 = 'h0; \
	sel_ALU = 'h0; \
	sel_BUS1 = 'h0; \
	sel_MEM = 'h0; \
	load_add_R = 'h0; \
	load_reg_Y = 'h0; \
	load_reg_Z = 'h0; \
	load_IR = 'h0; \
	inc_PC  = 'h0; \
	sel_R0  = 'h0; \
	sel_R1  = 'h0; \
	sel_R2  = 'h0; \
	sel_R3  = 'h0; \
	load_R0 = 'h0; \
	load_R1 = 'h0; \
	load_R2 = 'h0; \
	load_R3 = 'h0; \
	load_PC = 'h0; \
	sel_ALU = 'h0; \
	write   = 'h0; \
end

	
module ctrl_unit #(
	`include "C:/Users/gaoji/Desktop/RISC_SPM/src/para_def.v"
)(
	output reg 			      load_R0, load_R1, load_R2, load_R3,
	output reg				  load_PC, 
	output reg			      load_IR,
	output reg				  load_reg_Y,
	output reg				  load_reg_Z,
	output reg				  load_add_R,
	output reg				  inc_PC,	
	output reg [SEL1_WD -1:0] mux1_sel_to_bus1,
	output reg [SEL2_WD -1:0] mux2_sel_to_bus2,
	
	output reg				  write,
	
	input  [WORD_WD -1:0]     instruction,
	input 			          zero_flag,
	input 				      clk, rst_n
);

	reg [STATE_WD -1:0] state, next_state;
	wire[OPCODE_WD -1:0] opcode;
	wire[SRC_WD -1:0]    src;
	wire[DEST_WD -1:0]   dest;
	reg sel_R0, sel_R1, sel_R2, sel_R3, sel_PC;
	reg sel_ALU, sel_BUS1, sel_MEM;
	
	assign opcode = instruction[(WORD_WD -1) : (WORD_WD -OPCODE_WD)];
	assign src    = instruction[(SRC_WD +DEST_WD -1) : DEST_WD];
	assign dest   = instruction[DEST_WD -1:0];
	
	always @(*)begin
		if(sel_R0)      mux1_sel_to_bus1 = 0;
		else if(sel_R1) mux1_sel_to_bus1 = 1;
		else if(sel_R2) mux1_sel_to_bus1 = 2;
		else if(sel_R3) mux1_sel_to_bus1 = 3;
		else if(sel_PC) mux1_sel_to_bus1 = 4;
		else			mux1_sel_to_bus1 = 0;
	end

	always @(*)begin
		if(sel_ALU)       mux2_sel_to_bus2 = 0;
		else if(sel_BUS1) mux2_sel_to_bus2 = 1;
		else if(sel_MEM)  mux2_sel_to_bus2 = 2;
		else			  mux2_sel_to_bus2 = 0;
	end			

	always @(posedge clk)begin
		if(rst_n == 1'b0)begin
			state <= 'h0;
		end
		else begin
			state <= next_state;
		end
	end
	
	always @(*)begin
	    case(state)
			S_IDLE: next_state = S_FET1;
			S_FET1: next_state = S_FET2;
			S_FET2: next_state = S_DEC;
			S_DEC : begin
				case(opcode)
					NOP, NOT:           next_state = S_FET1;
					ADD, SUB, AND, NOT: next_state = S_EX1;
					RD:					next_state = S_RD1;
					WR:					next_state = S_WR1;
					BR:					next_state = S_BR1;
					BRZ: 				if(zero_flag == 0) next_state = S_BR1;
					default:		    next_state = S_FET1;
				endcase
			end
			S_EX1 : next_state = S_FET1;
			S_RD1 : next_state = S_RD2;
			S_RD2 : next_state = S_FET1;
			S_WR1 : next_state = S_WR2;
			S_WR2 : next_state = S_FET1;
			S_BR1 : next_state = S_BR2;
			S_BR2 : next_state = S_FET1;
			default:next_state = S_IDLE;
		endcase
	end
	
	always @(*)begin
		`ASSIGN_ZERO
		case(state)
			S_IDLE: ;
			S_FET1: begin
				sel_PC     = 'h1;
				sel_BUS1   = 'h1;
				load_add_R = 'h1;
			end
			S_FET2: begin
				sel_MEM = 'h1;
				load_IR = 'h1;
				inc_PC  = 'h1;
			end
			S_DEC:  begin
				case(opcode)
					NOP: ;
					ADD, SUB, AND: begin
						case(src)
							SEL_R0: sel_R0 = 'h1;
							SEL_R1: sel_R1 = 'h1;
							SEL_R2: sel_R2 = 'h1;
							SEL_R3: sel_R3 = 'h1;
							default:;
						endcase
						sel_BUS1   = 'h1;
						load_reg_Y = 'h1;
					end
					NOT: begin
						case(src)
							SEL_R0: sel_R0 = 'h1;
							SEL_R1: sel_R1 = 'h1;
							SEL_R2: sel_R2 = 'h1;
							SEL_R3: sel_R3 = 'h1;
							default:;
						endcase
						case(dest)
							SEL_R0: load_R0 = 'h1;
							SEL_R1: load_R1 = 'h1;
							SEL_R2: load_R2 = 'h1;
							SEL_R3: load_R3 = 'h1;
							default:;
						endcase
						load_reg_Z = 'h1;
						sel_ALU    = 'h1;
					end
					RD:  begin
						sel_PC     = 'h1;
						sel_BUS1   = 'h1;
						load_add_R = 'h1;
					end
					WR:  begin
						sel_PC     = 'h1;
						sel_BUS1   = 'h1;
						load_add_R = 'h1;
					end
					BR:  begin
						sel_PC     = 'h1;
						sel_BUS1   = 'h1;
						load_add_R = 'h1;
					end
					default:;
				endcase
			end
			S_EX1:  begin
				case(dest)
					SEL_R0: begin sel_R0 = 'h1; load_R0 = 'h1; end
					SEL_R1: begin sel_R1 = 'h1; load_R1 = 'h1; end
					SEL_R2: begin sel_R2 = 'h1; load_R2 = 'h1; end
					SEL_R3: begin sel_R3 = 'h1; load_R3 = 'h1; end
					default:;
				endcase
				sel_ALU    = 'h1;
				load_reg_Z = 'h1;
			end
			S_RD1:  begin
				sel_MEM = 'h1;
				load_add_R = 'h1;
				inc_PC  = 'h1;
			end
			S_RD2:  begin
				sel_MEM = 'h1;
				case(dest)
					SEL_R0: load_R0 = 'h1;
					SEL_R1: load_R1 = 'h1;
					SEL_R2: load_R2 = 'h1;
					SEL_R3: load_R3 = 'h1;
					default:;
				endcase
			end
			S_WR1:  begin
				sel_MEM    = 'h1;
				load_add_R = 'h1;
				inc_PC     = 'h1;
			end
			S_WR2:  begin
				load_add_R = 'h1;
				write	   = 'h1;
				case(src)
					SEL_R0: sel_R0 = 'h1;
					SEL_R1: sel_R1 = 'h1;
					SEL_R2: sel_R2 = 'h1;
					SEL_R3: sel_R3 = 'h1;
					default:;
				endcase
			end
			S_BR1:  begin
				sel_MEM    = 'h1;
				load_add_R = 'h1;
			end
			S_BR2:  begin
				sel_MEM    = 'h1;
				load_PC    = 'h1;
			end			
		endcase
	end
	
endmodule

`endif